/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
/* Copyright (C) 2012 Zongyou Yao. All rights reserved.     */

#ifndef MEM_INCLUDED
#define MEM_INCLUDED

#include "except.h"

/**
 * It is a checked runtime error to pass nbytes <= 0 to any mem function or macro.
 */

extern const except_t mem_failed;

/**
 * Allocates nbytes bytes and returns a pointer to the first byte. The bytes
 * are uninitialized. If mem_alloc raise mem_failed, file and line are reported
 * as the offending source coordinates.
 * Exception mem_failed can be raised.
 */
void * mem_alloc(long nbytes, const char *file,  int line);

/**
 * Allocates space for an array of count elements, each occupying
 * nbytes bytes, and returns a pointer to the first element. It is
 * a checked runtime error for count <= 0. The elements are cleared,
 * which does not necessarily initialize pointers to null or
 * floating-point values to 0.0. If mem_calloc raises mem_failed, file
 * and line are reported as the offending source coordinate.
 * Exception mem_failed can be raised.
 */
void * mem_calloc(long count, long nbytes, const char *file, int line);

/**
 * Deallocates ptr, if ptr is nonnull. It is an unchecked runtime error for ptr
 * to be a pointer that was not returned by a previous call to a mem allocation
 * function. Implementations may use file and line to report memory-usage errors.
 */
void mem_free(void *ptr, const char *file, int line);

/**
 * Changes the size of the block at ptr to hold nbytes bytes, and returns a pointer
 * to the first byte of the new block. If nbytes exceeds the size of the original block,
 * the excess bytes are uninitialized. If nbytes is less than the size of the original
 * block, only nbytes of its bytes appear in the new block. If mem_resize raises mem_failed,
 * file and line are reported as the offending source coordinates. It is a checked runtime
 * error for ptr == null, and it is an unchecked runtime error for ptr to be a pointer that
 * was not returned by a previous call to a mem allocation function.
 * Exception mem_failed can be raised.
 */
void *mem_resize(void *ptr, long nbytes, const char *file, int line);

/**
 * Allocates nbytes bytes and returns a pointer to the first byte.
 * The bytes are uninitialized. See mem_alloc.
 * Exception mem_failed can be raised.
 */
#define ALLOC(nbytes)                  mem_alloc((nbytes), __FILE__, __LINE__)

/**
 * Allocates space for an array of count elements, each occupying nbytes bytes,
 * and returns a pointer to the first element. It is a checked runtime error for
 * count <= 0. The elements are cleared. See mem_alloc.
 * Exception mem_failed can be raised.
 */
#define CALLOC(count, nbytes)          mem_calloc((count), (nbytes), __FILE__, __LINE__)

/**
 * Allocate a block large enough to hold *p, set p to the address of the block,
 * and return that address. NEW0 clears the bytes, and NEW leaves them uninitialized.
 * Both macros evaluate ptr once.
 * Exception mem_failed can be raised.
 */
#define NEW(p)                         ((p) = ALLOC((long)sizeof *(p)))
#define NEW0(p)                        ((p) = CALLOC(1, (long)sizeof *(p)))

/**
 * Deallocates ptr, if ptr is nonnull, and clears ptr. ptr is evaluated
 * more than once. See mem_free.
 */
#define FREE(ptr)                      ((void)(mem_free((ptr), __FILE__, __LINE__), (ptr) = 0))

/**
 * Changes the size of the block at ptr to hold nbytes bytes, reaims ptr at the
 * resized block, and returns the address of the block. ptr is evaluated more
 * than once. See mem_resize.
 * Exception mem_failed can be raised.
 */
#define RESIZE(ptr, nbytes)            ((ptr) = mem_resize((ptr), (nbytes), __FILE__, __LINE__))

#endif
